package base.thread_demo.demo11;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 哲学家吃饭问题死锁演示: 通过 synchronized 互斥资源锁方式,很容易出现死锁问题
 */
public class PhilosopherEat {
    public static void main(String[] args) {
        Chopstick c1 = new Chopstick("1");
        Chopstick c2 = new Chopstick("2");
        Chopstick c3 = new Chopstick("3");
        Chopstick c4 = new Chopstick("4");
        Chopstick c5 = new Chopstick("5");

        new Philosopher("苏格拉底", c1, c2).start();
        new Philosopher("柏拉图", c2, c3).start();
        new Philosopher("亚里士多德", c3, c4).start();
        new Philosopher("赫拉克利特", c4, c5).start();
        new Philosopher("阿基米德", c5, c1).start();

        // 修改以下这种方式不会出现死锁: 大家都按照筷子从小到大顺序获取筷子
        // 以下这种方式"赫拉克利特"大概率处于执行状态. 演示了线程的活跃性-饥饿性
        //new Philosopher("阿基米德", c1, c5).start();
    }
}


/**
 * 哲学家类作为要执行的吃饭和思考两任务,让其继承 Thread, 哲学家类对象就可以直接当做线程任务执行
 */
class Philosopher extends Thread {
    private static final Logger log = LoggerFactory.getLogger(Philosopher.class);

    private Chopstick left, right;

    public Philosopher(String name, Chopstick left, Chopstick right){
        super(name);
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while(true) {
            // 尝试获得左手筷子
            synchronized(left) {
                // 尝试获得右手筷子
                synchronized (right) {
                    eat();
                }
            }
        }
    }

    public void eat() {
        log.debug("{} 在吃饭...", Thread.currentThread().getName());
        try {
            Thread.sleep(500);// 哲学家在思考
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 筷子类作为共享资源,需要上锁保护, 让其继承 ReentrantLock，筷子类对象就可以尝试获取锁
 */
class Chopstick extends ReentrantLock {
    private String name;
    public Chopstick(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "筷子{" + name + '}';
    }
}
